/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.io;

import org.vacoor.nothing.common.codec.Hex;
import org.vacoor.nothing.common.util.Throwables;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.spi.AbstractInterruptibleChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 消息摘要 Channel
 *
 * @author vacoor
 */
public class MessageDigestWritableByteChannel extends AbstractInterruptibleChannel implements WritableByteChannel {
    private final MessageDigest digester;

    private volatile boolean hasContent;
    private byte[] hashed;
    private String hexEncoded;

    public static MessageDigestWritableByteChannel of(String algorithm) {
        return new MessageDigestWritableByteChannel(algorithm);
    }

    public MessageDigestWritableByteChannel(String algorithm) {
        if (algorithm == null) {
            throw new NullPointerException("algorithm argument cannot be null.");
        }

        try {
            digester = MessageDigest.getInstance(algorithm);
            digester.reset();
        } catch (NoSuchAlgorithmException e) {
            throw Throwables.unchecked(e);
        }
    }

    @Override
    public int write(ByteBuffer byteBuffer) throws IOException {
        if (!hasContent) hasContent = true;
        digester.update(byteBuffer);
        return byteBuffer.remaining();
    }

    @Override
    protected void implCloseChannel() throws IOException {
    }

    public byte[] getHash() {
        if (!hasContent) {
            return new byte[0];
        }

        if (hashed == null) {
            hashed = digester.digest();
        }
        byte[] copy = new byte[hashed.length];
        System.arraycopy(hashed, 0, copy, 0, hashed.length);
        return copy;
    }

    public String getHashAsHex() {
        if (hexEncoded == null) {
            hexEncoded = Hex.encode(getHash());
        }
        return hexEncoded;
    }
}
